#include "GVector3.h"

#include "GMath.h"

namespace GMath
{

	const GVector3 GVector3::Zero	= GVector3(0.0f, 0.0f, 0.0f);
	const GVector3 GVector3::One	= GVector3(1.0f, 1.0f, 1.0f);
	const GVector3 GVector3::UnitX	= GVector3(1.0f, 0.0f, 0.0f);
	const GVector3 GVector3::UnitY	= GVector3(0.0f, 1.0f, 0.0f);
	const GVector3 GVector3::UnitZ	= GVector3(0.0f, 0.0f, 1.0f);
	const GVector3 GVector3::Min	= GVector3(G_FLOAT_MIN, G_FLOAT_MIN, G_FLOAT_MIN);
	const GVector3 GVector3::Max	= GVector3(G_FLOAT_MAX, G_FLOAT_MAX, G_FLOAT_MAX);

	GVector3::GVector3()
	{ 
		x = 0.0f;
		y = 0.0f;
		z = 0.0f;
	}

	GVector3::GVector3(float Vx, float Vy, float Vz)
	{
		x = Vx;
		y = Vy;
		z = Vz;
	}

	GVector3::operator float* ()
	{
		return Arr;
	}

	GVector3::operator const float* ()const
	{
		return (const float*)Arr;
	}

	float GVector3::Lenght() const
	{
		return SqrtF(x * x + y * y + z * z);
	}

	float GVector3::LenghtSqr() const
	{
		return x * x + y * y + z * z;
	}

	float GVector3::Distance(const GVector3& Vector) const
	{
		float Dx = Vector.x - x;
		float Dy = Vector.y - y;
		float Dz = Vector.z - z;
		return SqrtF(Dx * Dx + Dy * Dy + Dz * Dz);
	}

	float GVector3::DistanceSqr(const GVector3& Vector) const
	{
		float Dx = Vector.x - x;
		float Dy = Vector.y - y;
		float Dz = Vector.z - z;
		return Dx * Dx + Dy * Dy + Dz * Dz;
	}

	void GVector3::Maximum(OUTPUT GVector3& Output, const GVector3& Vector1, const GVector3& Vector2)
	{
		Output.x = Vector1.x > Vector2.x ? Vector1.x : Vector2.x;
		Output.y = Vector1.y > Vector2.y ? Vector1.y : Vector2.y;
		Output.z = Vector1.z > Vector2.z ? Vector1.z : Vector2.z;
	}

	void GVector3::Minimum(OUTPUT GVector3& Output, const GVector3& Vector1, const GVector3& Vector2)
	{
		Output.x = Vector1.x < Vector2.x ? Vector1.x : Vector2.x;
		Output.y = Vector1.y < Vector2.y ? Vector1.y : Vector2.y;
		Output.z = Vector1.z < Vector2.z ? Vector1.z : Vector2.z;
	}

	void GVector3::Saturate()
	{
		Clamp(GVector3::Zero, GVector3::One);
	}

	void GVector3::Saturate(OUTPUT GVector3& Output, const GVector3& Vector)
	{
		GVector3::Clamp(Output, Vector, GVector3::Zero, GVector3::One);
	}

	void GVector3::Normalize()
	{
		// Check zero case
		float InverseLenght = 1 / Lenght();

		x *= InverseLenght;
		y *= InverseLenght;
		z *= InverseLenght;
	}

	void GVector3::Normalize(OUTPUT GVector3& Output, const GVector3& Vector)
	{
		// Check zero case
		float InverseLenght = 1 / Vector.Lenght();

		Output.x = Vector.x * InverseLenght;
		Output.y = Vector.y * InverseLenght;
		Output.z = Vector.z * InverseLenght;
	}

	void GVector3::Absolute()
	{
		x = AbsF(x);
		y = AbsF(y);
		z = AbsF(z);
	}

	void GVector3::Absolute(OUTPUT GVector3& Output, const GVector3& Vector)
	{
		Output.x = AbsF(Vector.x);
		Output.y = AbsF(Vector.y);
		Output.z = AbsF(Vector.z);
	}

	void GVector3::Scale(float Value)
	{
		x *= Value;
		y *= Value;
		z *= Value;
	}

	void GVector3::Scale(OUTPUT GVector3& Output, const GVector3& Vector, float Value)
	{
		Output.x = Vector.x * Value;
		Output.y = Vector.y * Value;
		Output.z = Vector.z * Value;
	}

	void GVector3::Clamp(const GVector3& LimitLow, const GVector3& LimitUp)
	{
		if (x > LimitUp.x)
		{
			x = LimitUp.x;
		}
		else if (x < LimitLow.x)
		{
			x = LimitLow.x;
		}

		if (y > LimitUp.y)
		{
			y = LimitUp.y;
		}
		else if (y < LimitLow.y)
		{
			y = LimitLow.y;
		}

		if (z > LimitUp.z)
		{
			z = LimitUp.z;
		}
		else if (z < LimitLow.z)
		{
			z = LimitLow.z;
		}
	}

	void GVector3::Clamp(OUTPUT GVector3& Output, const GVector3& Vector, const GVector3& LimitLow, const GVector3& LimitUp)
	{
		if (Vector.x > LimitUp.x)
		{
			Output.x = LimitUp.x;
		}
		else if (Vector.x < LimitLow.x)
		{
			Output.x = LimitLow.x;
		}

		if (Vector.y > LimitUp.y)
		{
			Output.y = LimitUp.y;
		}
		else if (Vector.y < LimitLow.y)
		{
			Output.y = LimitLow.y;
		}

		if (Vector.z > LimitUp.z)
		{
			Output.z = LimitUp.z;
		}
		else if (Vector.z < LimitLow.z)
		{
			Output.z = LimitLow.z;
		}
	}

	void GVector3::Lerp(const GVector3& Vector, float Amount)
	{
		x += (Vector.x - x) * Amount;
		y += (Vector.y - y) * Amount;
		z += (Vector.z - z) * Amount;
	}

	void GVector3::Lerp(OUTPUT GVector3& Output, const GVector3& VectorA, const GVector3& VectorB, float Amount)
	{
		Output.x = VectorA.x + (VectorB.x - VectorA.x) * Amount;
		Output.y = VectorA.y + (VectorB.y - VectorA.y) * Amount;
		Output.z = VectorA.z + (VectorB.z - VectorA.z) * Amount;
	}

	float GVector3::Dot(const GVector3& Vector) const
	{
		return (x * Vector.x) + (y * Vector.y) + (z * Vector.z);
	}

	float GVector3::Dot(const GVector3& VectorA, const GVector3& VectorB)
	{
		return (VectorA.x * VectorB.x) + (VectorA.y * VectorB.y) + (VectorA.z * VectorB.z);
	}

	void GVector3::Cross(const GVector3& Vector)
	{
		GVector3 Temp(x, y, z);
		x = Temp.y * Vector.z - Temp.z * Vector.y;
		y = Temp.z * Vector.x - Temp.x * Vector.z;
		z = Temp.x * Vector.y - Temp.y * Vector.x;
	}

	void GVector3::Cross(OUTPUT GVector3& Output, const GVector3& VectorA, const GVector3& VectorB)
	{
		Output.x = VectorA.y * VectorB.z - VectorA.z * VectorB.y;
		Output.y = VectorA.z * VectorB.x - VectorA.x * VectorB.z;
		Output.z = VectorA.x * VectorB.y - VectorA.y * VectorB.x;
	}

	GVector3& GVector3::operator += (const GVector3& Vector)
	{
		x = x + Vector.x;
		y = y + Vector.y;
		z = z + Vector.z;
		return *this;
	}
	
	GVector3& GVector3::operator -= (const GVector3& Vector)
	{
		x = x - Vector.x;
		y = y - Vector.y;
		z = z - Vector.z;
		return *this;
	}

	GVector3& GVector3::operator *= (const float Value)
	{
		x = x * Value;
		y = y * Value;
		z = z * Value;
		return *this;
	}	

	GVector3& GVector3::operator *= (const GVector3& Vector)
	{
		x *= Vector.x;
		y *= Vector.y;
		z *= Vector.z;
		return *this;
	}

	GVector3& GVector3::operator /= (const float Value)
	{
		float Inverse = 1.0f / Value;
		x = x * Inverse;
		y = y * Inverse;
		z = z * Inverse;
		return *this;
	}

	GVector3 GVector3::operator + () const
	{
		return *this;
	}
	
	GVector3 GVector3::operator - () const
	{
		return GVector3(-x, -y, -z);
	}

	GVector3 GVector3::operator + (const GVector3& Vector) const
	{
		return GVector3(x + Vector.x, y + Vector.y, z + Vector.z);
	}

	GVector3 GVector3::operator - (const GVector3& Vector) const
	{
		return GVector3(x - Vector.x, y - Vector.y, z - Vector.z);
	}	

	GVector3 GVector3::operator * (const GVector3& Vector) const
	{
		return GVector3(x * Vector.x, y * Vector.y, z * Vector.z);
	}

	GVector3 GVector3::operator * (float Value) const
	{
		return GVector3(x * Value, y * Value, z * Value);
	}

	GVector3 GVector3::operator / (float Value) const
	{
		float Inverse = 1.0f / Value;
		return GVector3(x * Inverse, y * Inverse, z * Inverse);
	}

	bool GVector3::operator == (const GVector3& Vector) const
	{
		return	EqualF(x, Vector.x) &&
				EqualF(y, Vector.y) &&
				EqualF(z, Vector.z);
	}

	bool GVector3::operator != (const GVector3& Vector) const
	{
		return !operator==(Vector);
	}

	GVector3 operator * (float Value, const GVector3& Vector)
	{
		return GVector3(Value * Vector.x, Value * Vector.y, Value * Vector.y);
	}

	GVector3 operator / (float Value, const GVector3& Vector)
	{
		return GVector3(Value / Vector.x, Value / Vector.y, Value / Vector.y);
	}

}; // namespace GMath